namespace ge{

	class MS3D{
	protected:

		#pragma pack (push,1)

		struct Header {
		
			char id[11];
			int version;
			
			bool load(FILE* f){
				fread(id,10,1,f);
				id[10] = 0;
				fread(&version,4,1,f);
				return (version == 4) && (string(id)=="MS3D000000");
			}
			
		};
		
		struct Vertex {
			char v0;
			vec3 position;
			char boneid;
			char v1;
		};
		
		struct Face {
			short r0;
			unsigned short vertex_index[3];
			vec3 vertex_normal[3];
			float s[3];
			float t[3];
			short r1;
		};
		
		struct Group {
			char name[32];
			unsigned short face_count;
			vector <short> face_index;
			char material_index;
		};
		
		struct Material {
			char name[32];
			vec4 ambient;
			vec4 diffuse;
			vec4 specular;
			vec4 emissive;
			float shininess;
			float transparency;
			char mode;
			char texture[128];
			char alphamap[128];
		};
		
		struct Key {
			float time;
			vec3 value;
		};
		
		struct Joint {
			char name[32];
			char parent_name[32];
			vec3 rotation;
			vec3 position;
			vector <Key> rot_key;
			vector <Key> trans_key;
			
			mat4 absolute_matrix;
			mat4 inv_absolute_matrix;
		};
		
		#pragma pack (pop)

	public:

		static bool import(Model& model, const string& file_name){
			
			vector <Vertex> vertex;
			vector <Face> face;
			vector <Group> group;
			vector <Material> material;
			vector <Joint> joint;

			bool result = false;
			model.clear();
			FILE* f = fopen(file_name.c_str(),"rb");
			if (f) {
				Header header;
				if (header.load(f)){
					unsigned short count;
					// read vertex
					fread(&count,2,1,f);
					vertex.resize(count);
					fread(&vertex[0],sizeof(Vertex),count,f);
					// read face
					fread(&count,2,1,f);
					face.resize(count);
					fread(&face[0],sizeof(Face),count,f);
					// read group
					fread(&count,2,1,f);
					group.resize(count);
					for (unsigned short i=0;i<count;i++){
						fseek(f,1,SEEK_CUR);
						fread(group[i].name,34,1,f);
						group[i].face_index.resize(group[i].face_count);
						fread(&group[i].face_index[0],group[i].face_count*2,1,f);
						fread(&group[i].material_index,1,1,f);
					}
					// material
					fread(&count,2,1,f);
					material.resize(count);
					fread(&material[0],361,count,f);
					
					// joint (animation)
					float fps;
					float time;
					int total_frame;

					fread(&fps,4,1,f);
					fread(&time,4,1,f);
					fread(&total_frame,4,1,f);

					fread(&count,2,1,f);
					joint.resize(count);
					model.animation.resize(count);
					model.animation.set_length((float)total_frame/fps);
					model.animation.set_speed(1);
					for (unsigned int i = 0; i != joint.size(); i++){
						fseek(f,1,SEEK_CUR);
						fread(joint[i].name,88,1,f);
						joint[i].name[31] = 0;
						joint[i].parent_name[31] = 0;
						fread(&count,2,1,f);
						joint[i].rot_key.resize(count);
						fread(&count,2,1,f);
						joint[i].trans_key.resize(count);
						fread(&joint[i].rot_key[0],joint[i].rot_key.size(),sizeof(Key),f);
						fread(&joint[i].trans_key[0],joint[i].trans_key.size(),sizeof(Key),f);
						
						short parent = -1;

						// находим предка					
						for (unsigned short k = 0; k != i; k++)
							if (string(joint[i].parent_name) == joint[k].name) {
								parent = k;
								break;
							}
						// рассчет Absolute matrix
						// поворот
						mat4 m = toMat4(quat(joint[i].rotation));//eulerAngleYXZ(joint[i].rotation.y,joint[i].rotation.x,joint[i].rotation.z);
						// смещение
						m[3][0] = joint[i].position.x;
						m[3][1] = joint[i].position.y;
						m[3][2] = joint[i].position.z;			
						// запоминаем найденное как релативную матрицу
						model.animation.joint(i).relative_matrix = m;
						// находим инверсию абсолютной матрицы
						joint[i].absolute_matrix = m;
						if (parent)
							joint[i].absolute_matrix = joint[parent].absolute_matrix * joint[i].absolute_matrix;
						joint[i].inv_absolute_matrix = glm::inverse(joint[i].absolute_matrix);
						// задаем ключевые кадры
						for (vector<Key>::iterator it = joint[i].rot_key.begin(); it != joint[i].rot_key.end();it++)
							model.animation.joint(i).rotate_key[(*it).time] = quat((*it).value);
						for (vector<Key>::iterator it = joint[i].trans_key.begin(); it != joint[i].trans_key.end();it++)
							model.animation.joint(i).translate_key[(*it).time] = (*it).value;
					
						// задаем начальные значения
						model.animation.joint(i).translate = vec3(0,0,0);
						model.animation.joint(i).rotate = quat(0,0,0,1);
						model.animation.joint(i).parent = parent;
						
					}
					
					// convert to model
					for (vector<Group>::iterator gp=group.begin();gp != group.end();++gp){
						(*gp).name[31] = 0;
						string name = (*gp).name;
						Object* obj = &model.child(name);
						
						// material
						int midx = (*gp).material_index;
						material[midx].texture[127] = 0;
						
						if (Scene::active_scene()) {
							Scene3D* scene = (Scene3D*)Scene::active_scene();
							if ((*scene).material.find(material[midx].name) != (*scene).material.end()) {
								obj->material = &((*scene).material[material[midx].name]);
								obj->material->ambient = material[midx].ambient;
								obj->material->diffuse = material[midx].diffuse;
								obj->material->specular = material[midx].specular;
								obj->material->emission = material[midx].emissive;
								obj->material->shininess = material[midx].shininess;
								obj->material->color = vec4(1,1,1,material[midx].transparency);
								obj->material->texture.load_image(material[midx].texture);
							} else {
								obj->material = &((*scene).material[material[midx].name]);
							}
						}
				
						// face
						obj->mesh.clear();
						short bone;
						vec3 vx,nx;

						for (unsigned int i=0;i!=(*gp).face_count;i++){
							Face* fc = &face[(*gp).face_index[i]];
							
							bone = vertex[(*fc).vertex_index[0]].boneid;
							vx = vertex[(*fc).vertex_index[0]].position;
							nx = (*fc).vertex_normal[0];
							if (bone>=0){
								vx = vec3( joint[bone].inv_absolute_matrix * vec4(vx,1) );
								nx = mat3( joint[bone].inv_absolute_matrix ) * nx;
							}
							obj->mesh.bone(bone);
							obj->mesh.texcoord((*fc).s[0],-(*fc).t[0]);
							obj->mesh.normal(nx);	
							obj->mesh.vertex(vx);

							bone = vertex[(*fc).vertex_index[1]].boneid;
							vx = vertex[(*fc).vertex_index[1]].position;
							nx = (*fc).vertex_normal[1];
							if (bone>=0){
								vx = vec3( joint[bone].inv_absolute_matrix * vec4(vx,1) );
								nx = mat3( joint[bone].inv_absolute_matrix ) * nx;
							}
							obj->mesh.bone(bone);
							obj->mesh.texcoord((*fc).s[1],-(*fc).t[1]);
							obj->mesh.normal(nx);	
							obj->mesh.vertex(vx);

							bone = vertex[(*fc).vertex_index[2]].boneid;
							vx = vertex[(*fc).vertex_index[2]].position;
							nx = (*fc).vertex_normal[2];
							if (bone>=0){
								vx = vec3( joint[bone].inv_absolute_matrix * vec4(vx,1) );
								nx = mat3( joint[bone].inv_absolute_matrix ) * nx;
							}
							obj->mesh.bone(bone);
							obj->mesh.texcoord((*fc).s[2],-(*fc).t[2]);
							obj->mesh.normal(nx);	
							obj->mesh.vertex(vx);
							
							
						} // face

					}// group
					
					result = true;
				}
				fclose(f);
			}
			return result;
		}
		
	};
	
}
